﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using DonutLandLibrary.Robots.Environments;
using System.Threading;

namespace DonutLandLibrary.Data
{
    public class EnvironmentsData
    {
        public static List<int> GetEnvironments()
        {
            RobotsEntities entities = new RobotsEntities();
            List<int> environmentIDs = new List<int>();

            List<Environment> environments;// = new List<RobotEnvironment>();

            environments = entities.Environments.ToList();

            foreach (Environment environment in environments)
            {
                environmentIDs.Add(environment.ID);
            }

            return environmentIDs;
        }

        public static RobotEnvironment GetEnvironment(int EnvironmentID)
        {
            RobotsEntities entities = new RobotsEntities();

            Environment environment;
            RobotEnvironment robotEnvironment;
            SquareCollection squareCollection;
            List<EnvironmentSquare> environmentSquares;
            List<Robot> robotCollection;
            Robots.GeneticAlgorithm.GeneticProfile geneProfile;
            Square robotSquare;

            environment = entities.Environments.Where(re => re.ID == EnvironmentID).FirstOrDefault();

            //do not need to populate robotEnvironment properties as they are hard
            //coded constants
            robotEnvironment = new RobotEnvironment();
            robotEnvironment.ID = environment.ID;
            robotEnvironment.CycleCount = environment.CycleCount;
            squareCollection = new SquareCollection(robotEnvironment.ColumnSize, robotEnvironment.RowSize);

            //Build the square collection;
            environmentSquares = entities.EnvironmentSquares.Where(es => es.EnvironmentID == EnvironmentID).ToList();

            foreach(EnvironmentSquare environmentSquare in environmentSquares)
            {
                robotSquare = squareCollection.retrieveSquareAtCoordinate(  environmentSquare.XCoordinate,
                                                                            environmentSquare.YCoordinate);

                //set the properties of the squares
                robotSquare.ID = environmentSquare.ID;
                robotSquare.PowerSources = environmentSquare.PowerSources;
                robotSquare.CorrosivePatchPresent = environmentSquare.CorrosivePatchPresent;
            }

            //assign the square collection to the robot environment
            robotEnvironment.Squares = squareCollection;

            //build the robot collection
            robotCollection = entities.Robots.Where(rb => rb.CurrentEnvironment == EnvironmentID && rb.RobotAlive == true).ToList();

            foreach (Robot environmentRobot in robotCollection)
            {
                geneProfile = new Robots.GeneticAlgorithm.GeneticProfile(environmentRobot.GeneticProfile);

                Robots.Robot robot = new Robots.Robot(geneProfile, environmentRobot.CycleAdded, robotEnvironment.CycleCount);

                robot.ID = environmentRobot.ID;
                robot.ProtectiveCasingReserve = environmentRobot.ProtectiveCasingReserve;
                robot.EnergyDepletionReserve = environmentRobot.EnergyDepletionReserve;
                robot.CurrentSquare = robotEnvironment.Squares.retrieveSquareByID(environmentRobot.CurrentSquare);
                robot.ProtectiveCasing = environmentRobot.ProtectiveCasing;
                robot.EnergyLevel = environmentRobot.EnergyLevel;
                robot.RobotAlive= environmentRobot.RobotAlive;
                robot.GeneticProfile = geneProfile;
                robot.CurrentEnvironment = robotEnvironment;
                robot.RobotDirection = (DonutLandLibrary.Robots.Robot.Direction)environmentRobot.RobotDirection;

                robotEnvironment.RobotCollection.Add(robot);
                robot.CurrentSquare.RobotsOnSquare.Add(robot);
            }

            return robotEnvironment;
        }

        public static int SaveEnvironment(RobotEnvironment robotEnvironment)
        {
            RobotsEntities entities = new RobotsEntities();

            //create the LINQ DB entity
            Environment environment = new Environment();

            //add the property values
            environment.MaxCorrisvePatches = robotEnvironment.MaxCorrosivePatches;
            environment.MaxPowerSources = robotEnvironment.MaxPowerSources;
            environment.MaxPowerSourcesPerSquare = robotEnvironment.MaxPowerSourcesPerSquare;
            environment.RowSize = robotEnvironment.RowSize;
            environment.ColumnSize = robotEnvironment.ColumnSize;
            environment.CycleCount = robotEnvironment.CycleCount;

            entities.AddToEnvironments(environment);

            //add each square
            foreach(SquareRowCollection environmentRows in robotEnvironment.Squares)
            {
                foreach (Square robotEnvironmentSquare in environmentRows)
                {
                   robotEnvironmentSquare.ID = EnvironmentSquaresData.AddRobotEnvironmentSquare(robotEnvironmentSquare, entities, environment.ID);
                }
            }

            entities.SaveChanges();

            return environment.ID;
        }

        public static void UpdateEnvironment(RobotEnvironment robotEnvironment)
        {
            RobotsEntities entities = new RobotsEntities();

            //get the LINQ DB entity
            Environment environment = entities.Environments.Where(en => en.ID == robotEnvironment.ID).FirstOrDefault();

            //add the property values
            environment.MaxCorrisvePatches = robotEnvironment.MaxCorrosivePatches;
            environment.MaxPowerSources = robotEnvironment.MaxPowerSources;
            environment.MaxPowerSourcesPerSquare = robotEnvironment.MaxPowerSourcesPerSquare;
            environment.RowSize = robotEnvironment.RowSize;
            environment.ColumnSize = robotEnvironment.ColumnSize;
            environment.CycleCount = robotEnvironment.CycleCount;

            //add each square
            robotEnvironment.Squares.Reset();

            //var handles = new ManualResetEvent[robotEnvironment.RowSize * robotEnvironment.ColumnSize];
            //int count = 0;

            foreach (SquareRowCollection environmentRows in robotEnvironment.Squares)
            {
                environmentRows.Reset();

                foreach (Square robotEnvironmentSquare in environmentRows)
                {
                    //ThreadState threadState = new ThreadState();

                    //threadState.Entities = entities;
                    //threadState.RobotEnvironmentSquare = robotEnvironmentSquare;
                    //threadState.ID = environment.ID;

                    //ThreadPool.SetMaxThreads(1600, 1600);

                    //handles[count] = new ManualResetEvent(false);
                    //var currentAction = new ThreadTask(SaveData);
                    //var currentHandle = handles[count];
                    //Action wrappedAction = () => { try { currentAction(threadState); } finally { currentHandle.Set(); } };
                    //ThreadPool.QueueUserWorkItem(x => wrappedAction());

                    //count++;

                    //ThreadState threadState = new ThreadState();

                    //threadState.Entities = entities;
                    //threadState.RobotEnvironmentSquare = robotEnvironmentSquare;
                    //threadState.ID = environment.ID;

                    //ThreadPool.QueueUserWorkItem(new WaitCallback(ThreadTask), threadState);
                    ////EnvironmentSquaresData.UpdateRobotEnvironmentSquare(robotEnvironmentSquare, entities, environment.ID);

                    EnvironmentSquaresData.UpdateRobotEnvironmentSquare(robotEnvironmentSquare, entities, environment.ID);
                }
            }

            //WaitHandle.WaitAll(handles);

            entities.SaveChanges();

            //return environment.ID;
        }

        //private delegate void ThreadTask(Object stateInfo);

        //private static void SaveData(Object stateInfo)
        //{
        //    ThreadState threadState = (ThreadState)stateInfo;

        //    EnvironmentSquaresData.UpdateRobotEnvironmentSquare(threadState.RobotEnvironmentSquare, threadState.Entities, threadState.ID);
        //}
    }

    struct ThreadState
    {
        private Square robotEnvironmentSquare;
        private RobotsEntities entities;
        private int id;

        public Square RobotEnvironmentSquare
        {
            get
            {
                return robotEnvironmentSquare;
            }
            set
            {
                robotEnvironmentSquare = value;
            }
        }

        public RobotsEntities Entities
        {
            get
            {
                return entities;
            }
            set
            {
                entities = value;
            }
        }

        public int ID
        {
            get
            {
                return id;
            }
            set
            {
                id = value;
            }
        }
    }
}
